package me.minetsh.imaging.crop.util;

import ohos.aafwk.ability.DataAbilityHelper;
import ohos.aafwk.ability.DataAbilityRemoteException;
import ohos.aafwk.content.Intent;
import ohos.agp.render.opengl.GLES1X;
import ohos.agp.utils.Matrix;
import ohos.app.Context;
import ohos.data.rdb.ValuesBucket;
import ohos.data.resultset.ResultSet;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.Rect;
import ohos.media.photokit.metadata.AVStorage;
import ohos.utils.net.Uri;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("unused")
public class Utils {
    private static final String TAG = Utils.class.getSimpleName();
    private static final int SIZE_DEFAULT = 2048;
    private static final int SIZE_LIMIT = 4096;
    /**
     * sInputImageWidth
     */
    public static int sInputImageWidth = 0;
    /**
     * sInputImageHeight
     */
    public static int sInputImageHeight = 0;

    /**
     * copyExifInfo
     *
     * @param context context
     * @param sourceUri sourceUri
     * @param saveUri saveUri
     * @param outputWidth outputWidth
     * @param outputHeight outputHeight
     */
    public static void copyExifInfo(Context context, Uri sourceUri, Uri saveUri, int outputWidth, int outputHeight) {
        if (sourceUri == null || saveUri == null) return;
        try {
            File sourceFile = Utils.getFileFromUri(context, sourceUri);
            File saveFile = Utils.getFileFromUri(context, saveUri);
            if (sourceFile == null || saveFile == null) {
                return;
            }
            String sourcePath = sourceFile.getCanonicalPath();
            String savePath = saveFile.getCanonicalPath();

            ExifInterface sourceExif = new ExifInterface(sourcePath);
            List<String> tags = new ArrayList<>();
            tags.add(ExifInterface.TAG_DATETIME);
            tags.add(ExifInterface.TAG_FLASH);
            tags.add(ExifInterface.TAG_FOCAL_LENGTH);
            tags.add(ExifInterface.TAG_GPS_ALTITUDE);
            tags.add(ExifInterface.TAG_GPS_ALTITUDE_REF);
            tags.add(ExifInterface.TAG_GPS_DATESTAMP);
            tags.add(ExifInterface.TAG_GPS_LATITUDE);
            tags.add(ExifInterface.TAG_GPS_LATITUDE_REF);
            tags.add(ExifInterface.TAG_GPS_LONGITUDE);
            tags.add(ExifInterface.TAG_GPS_LONGITUDE_REF);
            tags.add(ExifInterface.TAG_GPS_PROCESSING_METHOD);
            tags.add(ExifInterface.TAG_GPS_TIMESTAMP);
            tags.add(ExifInterface.TAG_MAKE);
            tags.add(ExifInterface.TAG_MODEL);
            tags.add(ExifInterface.TAG_WHITE_BALANCE);

            tags.add(ExifInterface.TAG_EXPOSURE_TIME);
            tags.add(ExifInterface.TAG_APERTURE);
            tags.add(ExifInterface.TAG_ISO);

            tags.add(ExifInterface.TAG_DATETIME_DIGITIZED);
            tags.add(ExifInterface.TAG_SUBSEC_TIME);
            tags.add(ExifInterface.TAG_SUBSEC_TIME_DIG);
            tags.add(ExifInterface.TAG_SUBSEC_TIME_ORIG);

            tags.add(ExifInterface.TAG_F_NUMBER);
            tags.add(ExifInterface.TAG_ISO_SPEED_RATINGS);
            tags.add(ExifInterface.TAG_SUBSEC_TIME_DIGITIZED);
            tags.add(ExifInterface.TAG_SUBSEC_TIME_ORIGINAL);

            ExifInterface saveExif = new ExifInterface(savePath);
            String value;
            for (String tag : tags) {
                value = sourceExif.getAttribute(tag);
                if (!TextUtils.isEmpty(value)) {
                    saveExif.setAttribute(tag, value);
                }
            }
            saveExif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH, String.valueOf(outputWidth));
            saveExif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH, String.valueOf(outputHeight));
            saveExif.setAttribute(ExifInterface.TAG_ORIENTATION,
                    String.valueOf(ExifInterface.ORIENTATION_UNDEFINED));

            saveExif.saveAttributes();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * getExifRotation
     *
     * @param file file
     * @return File
     */
    public static int getExifRotation(File file) {
        if (file == null) return 0;
        try {
            ExifInterface exif = new ExifInterface(file.getCanonicalPath());
            return getRotateDegreeFromOrientation(
                    exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED));
        } catch (IOException e) {
            Logger.e("An error occurred while getting the exif data: " + e.getMessage(), e);
        }
        return 0;
    }

    /**
     * getExifRotation
     *
     * @param context context
     * @param uri uri
     * @return ResultSet
     */
    public static int getExifRotation(Context context, Uri uri) {
        ResultSet cursor = null;
        String[] projection = {AVStorage.Images.Media.ID};
        DataAbilityHelper helper = DataAbilityHelper.creator(context);
        try {
            cursor = helper.query(uri, projection, null);
            if (cursor == null || !cursor.goToFirstRow()) {
                return 0;
            }
            return cursor.getInt(0);
        } catch (RuntimeException | DataAbilityRemoteException ignored) {
            return 0;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * getExifOrientation
     *
     * @param context context
     * @param uri uri
     * @return orientation
     */
    public static int getExifOrientation(Context context, Uri uri) {
        String authority = uri.getDecodedAuthority().toLowerCase();
        int orientation;
        if (authority.endsWith("media")) {
            orientation = getExifRotation(context, uri);
        } else {
            orientation = getExifRotation(getFileFromUri(context, uri));
        }
        return orientation;
    }

    /**
     * getRotateDegreeFromOrientation
     *
     * @param orientation orientation
     * @return degree
     */
    public static int getRotateDegreeFromOrientation(int orientation) {
        int degree = 0;
        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                degree = 90;
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                degree = 180;
                break;
            case ExifInterface.ORIENTATION_ROTATE_270:
                degree = 270;
                break;
            default:
                break;
        }
        return degree;
    }

    /**
     * getMatrixFromExifOrientation
     *
     * @param orientation orientation
     * @return Matrix
     */
    public static Matrix getMatrixFromExifOrientation(int orientation) {
        Matrix matrix = new Matrix();
        switch (orientation) {
            case ExifInterface.ORIENTATION_UNDEFINED:
                break;
            case ExifInterface.ORIENTATION_NORMAL:
                break;
            case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
                matrix.postScale(-1.0f, 1.0f);
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                matrix.postRotate(180.0f);
                break;
            case ExifInterface.ORIENTATION_FLIP_VERTICAL:
                matrix.postScale(1.0f, -1.0f);
                break;
            case ExifInterface.ORIENTATION_ROTATE_90:
                matrix.postRotate(90.0f);
                break;
            case ExifInterface.ORIENTATION_TRANSVERSE:
                matrix.postRotate(-90.0f);
                matrix.postScale(1.0f, -1.0f);
                break;
            case ExifInterface.ORIENTATION_TRANSPOSE:
                matrix.postRotate(90.0f);
                matrix.postScale(1.0f, -1.0f);
                break;
            case ExifInterface.ORIENTATION_ROTATE_270:
                matrix.postRotate(-90.0f);
                break;
        }
        return matrix;
    }

    /**
     * getExifOrientationFromAngle
     *
     * @param angle angle
     * @return angle
     */
    public static int getExifOrientationFromAngle(int angle) {
        int normalizedAngle = angle % 360;
        switch (normalizedAngle) {
            case 0:
                return ExifInterface.ORIENTATION_NORMAL;
            case 90:
                return ExifInterface.ORIENTATION_ROTATE_90;
            case 180:
                return ExifInterface.ORIENTATION_ROTATE_180;
            case 270:
                return ExifInterface.ORIENTATION_ROTATE_270;
            default:
                return ExifInterface.ORIENTATION_NORMAL;
        }
    }

    /**
     * ensureUriPermission
     *
     * @param context context
     * @param intent intent
     * @return Uri
     */
    @SuppressWarnings("ResourceType")
    public static Uri ensureUriPermission(Context context, Intent intent) {
        Uri uri = intent.getUri();
        return uri;
    }

    /**
     * Get image file from uri
     *
     * @param context The context
     * @param uri     The Uri of the image
     * @return Image file
     */
    public static File getFileFromUri(final Context context,
                                      final Uri uri) {
        String filePath = null;
        // MediaStore (and general)
        if ("content".equalsIgnoreCase(uri.getScheme())) {
            filePath = getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            filePath = uri.getDecodedPath();
        }
        if (filePath != null) {
            return new File(filePath);
        }
        return null;
    }

    public static class RawDocumentsHelper {
        /**
         * RAW_PREFIX
         */
        public static final String RAW_PREFIX = "raw:";

        /**
         * isRawDocId
         *
         * @param docId docId
         * @return docId
         */
        public static boolean isRawDocId(String docId) {
            return docId != null && docId.startsWith(RAW_PREFIX);
        }

        /**
         * getDocIdForFile
         *
         * @param file file
         * @return filePath
         */
        public static String getDocIdForFile(File file) {
            try {
                return RAW_PREFIX + file.getCanonicalPath();
            } catch (IOException e) {
                e.fillInStackTrace();
                return RAW_PREFIX;
            }
        }

        /**
         * getAbsoluteFilePath
         *
         * @param rawDocumentId rawDocumentId
         * @return rawDocumentId
         */
        public static String getAbsoluteFilePath(String rawDocumentId) {
            return rawDocumentId.substring(RAW_PREFIX.length());
        }
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context       The context.
     * @param uri           The Uri to query.
     * @param selection     (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {
        ResultSet resultSet = null;
        DataAbilityHelper helper = DataAbilityHelper.creator(context);
        try {
            resultSet = helper.query(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, new String[]{AVStorage.Images.Media.DATA, AVStorage.Images.Media.VOLUME_NAME}, null);
            if (resultSet != null && resultSet.goToFirstRow()) {
                // 获取id字段的值
                String id = resultSet.getString(resultSet.getColumnIndexForName(AVStorage.Images.Media.DATA));
                return id;
            }
        } catch (DataAbilityRemoteException e) {
            e.printStackTrace();
        } finally {
            resultSet.close();
        }

        return null;
    }

    /**
     * decodeSampledBitmapFromUri
     *
     * @param context context
     * @param sourceUri sourceUri
     * @param requestSize requestSize
     * @return bitmap
     */
    public static PixelMap decodeSampledBitmapFromUri(Context context, Uri sourceUri, int requestSize) {
        InputStream stream = null;
        PixelMap bitmap = null;
        DataAbilityHelper helper = DataAbilityHelper.creator(context);
        try {
            stream = helper.obtainInputStream(sourceUri);
            if (stream != null) {
                ImageSource.DecodingOptions options = new ImageSource.DecodingOptions();
                options.sampleSize = Utils.calculateInSampleSize(context, sourceUri, requestSize);
                bitmap = ImageSource.create(stream, new ImageSource.SourceOptions()).createPixelmap(options);
            }
        } catch (FileNotFoundException | DataAbilityRemoteException e) {
            Logger.e(e.getMessage());
        } finally {
            try {
                if (stream != null) {
                    stream.close();
                }
            } catch (IOException e) {
                Logger.e(e.getMessage());
            }
        }
        return bitmap;
    }

    /**
     * calculateInSampleSize
     *
     * @param context context
     * @param sourceUri sourceUri
     * @param requestSize requestSize
     * @return inSampleSize
     */
    public static int calculateInSampleSize(Context context, Uri sourceUri, int requestSize) {
        DataAbilityHelper helper = DataAbilityHelper.creator(context);
        // check image size
        FileDescriptor fd = null;
        PixelMap pixelMap = null;
//        options.inJustDecodeBounds = true;
        try {
            fd = helper.openFile(sourceUri, "r");
            pixelMap = ImageSource.create(fd, null).createPixelmap(null);
        } catch (FileNotFoundException | DataAbilityRemoteException ignored) {
        }
        int inSampleSize = 1;
        sInputImageWidth = pixelMap.getImageInfo().size.width;
        sInputImageHeight = pixelMap.getImageInfo().size.height;
        while (pixelMap.getImageInfo().size.width / inSampleSize > requestSize
                || pixelMap.getImageInfo().size.height / inSampleSize > requestSize) {
            inSampleSize *= 2;
        }
        return inSampleSize;
    }

    /**
     * getScaledBitmapForHeight
     *
     * @param bitmap bitmap
     * @param outHeight outHeight
     * @return ScaledBitmapForHeight
     */
    public static PixelMap getScaledBitmapForHeight(PixelMap bitmap, int outHeight) {
        float currentWidth = bitmap.getImageInfo().size.width;
        float currentHeight = bitmap.getImageInfo().size.height;
        float ratio = currentWidth / currentHeight;
        int outWidth = Math.round(outHeight * ratio);
        return getScaledBitmap(bitmap, outWidth, outHeight);
    }

    /**
     * getScaledBitmapForWidth
     *
     * @param bitmap bitmap
     * @param outWidth outWidth
     * @return ScaledBitmapForWidth
     */
    public static PixelMap getScaledBitmapForWidth(PixelMap bitmap, int outWidth) {
        float currentWidth = bitmap.getImageInfo().size.width;
        float currentHeight = bitmap.getImageInfo().size.height;
        float ratio = currentWidth / currentHeight;
        int outHeight = Math.round(outWidth / ratio);
        return getScaledBitmap(bitmap, outWidth, outHeight);
    }

    /**
     * getScaledBitmap
     *
     * @param bitmap bitmap
     * @param outWidth outWidth
     * @param outHeight outHeight
     * @return ScaledBitmap
     */
    public static PixelMap getScaledBitmap(PixelMap bitmap, int outWidth, int outHeight) {
        int currentWidth = bitmap.getImageInfo().size.width;
        int currentHeight = bitmap.getImageInfo().size.height;
        Matrix scaleMatrix = new Matrix();
        scaleMatrix.postScale((float) outWidth / (float) currentWidth,
                (float) outHeight / (float) currentHeight);
        return PixelMap.create(bitmap, new Rect(0, 0, currentWidth, currentHeight), null);
    }

    /**
     * getMaxSize
     *
     * @return maxSize
     */
    public static int getMaxSize() {
        int maxSize = SIZE_DEFAULT;
        int[] arr = new int[1];
        GLES1X.glGetIntegerv(GLES1X.GL_MAX_TEXTURE_SIZE, arr);
        if (arr[0] > 0) {
            maxSize = Math.min(arr[0], SIZE_LIMIT);
        }
        return maxSize;
    }

    /**
     * closeQuietly
     *
     * @param closeable closeable
     */
    public static void closeQuietly(Closeable closeable) {
        if (closeable == null) return;
        try {
            closeable.close();
        } catch (Throwable ignored) {
        }
    }

    /**
     * updateGalleryInfo
     *
     * @param context context
     * @param uri uri
     */
    public static void updateGalleryInfo(Context context, Uri uri) {
        DataAbilityHelper helper = DataAbilityHelper.creator(context);
        ValuesBucket values = new ValuesBucket();
        File file = getFileFromUri(context, uri);
        if (file != null && file.exists()) {
            values.putLong(AVStorage.Images.Media.SIZE, file.length());
        }
        try {
            helper.update(uri, values, null);
        } catch (DataAbilityRemoteException e) {
            e.printStackTrace();
        }
    }
}
